package com.lemon.boot.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.LinkedHashMultimap;
import com.lemon.boot.common.base.BaseServiceImpl;
import com.lemon.boot.common.consts.ApiConst;
import com.lemon.boot.common.entity.LoginUserDetail;
import com.lemon.boot.common.enums.RoleKeyEnum;
import com.lemon.boot.common.enums.StatusEnum;
import com.lemon.boot.common.utils.JwtUtil;
import com.lemon.boot.dao.RoleDAO;
import com.lemon.boot.dto.RoleDTO;
import com.lemon.boot.po.MenuResourcePO;
import com.lemon.boot.po.RoleMenuPO;
import com.lemon.boot.po.RolePO;
import com.lemon.boot.service.IMenuResourceService;
import com.lemon.boot.service.IRoleMenuService;
import com.lemon.boot.service.IRoleService;
import com.lemon.boot.service.ITenantMenuService;
import com.lemon.boot.vo.TenantMenuResourceVO;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 角色表 业务实现类
 *
 * @author Lemon
 * @since 2024-02-29
 */
@Log4j2
@Service
public class RoleServiceImpl extends BaseServiceImpl<RoleDAO, RolePO, RoleDTO> implements IRoleService {
    @Resource
    private ITenantMenuService iTenantMenuService;
    @Resource
    private IRoleMenuService iRoleMenuService;
    @Resource
    private IMenuResourceService iMenuResourceService;

    @Override
    public List<RolePO> getByUserId(Long userId, Long tenantId) {
        Map<String, Object> param = ApiConst.enableStatusAndUnableDelete();
        param.put("userId", userId);
        param.put("tenantId", tenantId);
        return this.baseMapper.getByUserId(param);
    }

    @Override
    public List<RolePO> getByCurrentTenantId(Long tenantId) {
        return this.query().select("id", "name")
                .eq("tenant_id", tenantId)
                .eq(StatusEnum.ENABLE.getColumn(), StatusEnum.ENABLE.getStatus())
                .list();
    }

    @Override
    public List<RolePO> getCommonByCurrentTenantId() {
        return this.query().select("id", "name")
                .eq("tenant_id", JwtUtil.getCurrentUser().getTenantId())
                .eq("role_key", RoleKeyEnum.COMMON.getRoleKey())
                .eq(StatusEnum.ENABLE.getColumn(), StatusEnum.ENABLE.getStatus())
                .list();
    }

    @Override
    public List<RolePO> getByTenantId(Long tenantId) {
        return this.query().select("id", "role_key").eq("tenant_id", tenantId).list();
    }

    @Override
    @Transactional
    public RolePO get(Long id) {
        //1.查询当前角色
        RolePO rolePO = this.baseMapper.selectById(id);

        //2.设置 roleKeyName
        List<String> roleKeyInfo = Arrays.stream(RoleKeyEnum.values()).filter(item -> item.getRoleKey().equals(rolePO.getRoleKey())).map(RoleKeyEnum::getInfo).collect(Collectors.toList());
        if (roleKeyInfo.size() == 1) {
            rolePO.setRoleKeyName(roleKeyInfo.get(0));
        }

        //3.查询当前角色下的租户菜单-树形数据
        List<TenantMenuResourceVO> treeTenantMenuResourceList = iTenantMenuService.getByRoleIdTenantId(id, rolePO.getTenantId());

        //4.设置菜单和资源
        if (!CollectionUtils.isEmpty(treeTenantMenuResourceList)) {
            rolePO.setMenuResourceList(treeTenantMenuResourceList);
        }

        return rolePO;
    }

    @Override
    public Page<?> page(RoleDTO roleDTO) {
        roleDTO.setTenantId(JwtUtil.getCurrentUser().getTenantId());
        return super.page(roleDTO);
    }

    @Override
    @Transactional
    public String add(RolePO rolePO) {
        return this.addOrEdit(rolePO);
    }

    @Override
    @Transactional
    public String edit(RolePO rolePO) {
        return this.addOrEdit(rolePO);
    }

    @Override
    @Transactional
    public String addOrEdit(RolePO rolePO) {
        //1.是否是新增
        boolean isAdd = rolePO.getId() == null;

        //2.当前用户
        LoginUserDetail currentUser = JwtUtil.getCurrentUser();

        //3.判断角色名称是否重复
        boolean exist = this.query()
                .select("1")
                .eq("name", rolePO.getName())
                .eq("tenant_id", currentUser.getTenantId())
                .ne(!isAdd, "id", rolePO.getId())
                .last("limit 1")
                .exists();
        if (exist) {
            return String.format("角色名称\"%s\"已存在！", rolePO.getName());
        }

        //4.添加时，角色设置默认值
        if (isAdd) {//4.1.新增时设置默认值
            rolePO.setTenantId(currentUser.getTenantId());
        } else {//4.2.修改时，需要删除中间表
            //4.3.删除角色菜单中间表
            iRoleMenuService.update().eq("role_id", rolePO.getId()).remove();
            //4.4.删除菜单资源中间表
            iMenuResourceService.update().eq("role_id", rolePO.getId()).remove();
        }

        String result = super.addOrEdit(rolePO);

        //5.获取菜单集合
        List<TenantMenuResourceVO> menuResourceList = rolePO.getMenuResourceList();
        if (!CollectionUtils.isEmpty(menuResourceList)) {
            //5.1.递归获取已选择的菜单ID
            List<Long> menuIdList = getSelectedMenuIdList(new LinkedList<>(), menuResourceList);
            if (!CollectionUtils.isEmpty(menuIdList)) {
                List<RoleMenuPO> roleMenuList = menuIdList.stream().distinct().map(item -> new RoleMenuPO().setRoleId(rolePO.getId()).setTenantMenuId(item)).collect(Collectors.toList());

                if (!CollectionUtils.isEmpty(roleMenuList)) {
                    //5.2.添加角色菜单中间表
                    iRoleMenuService.saveBatch(roleMenuList);

                    //5.3.获取已选择的菜单ID、资源ID
                    LinkedHashMultimap<Long, Long> menuIdResourceIdMap = getSelectedMenuIdResourceIdMap(LinkedHashMultimap.create(), menuResourceList);
                    if (!menuIdResourceIdMap.isEmpty()) {
                        List<MenuResourcePO> addMenuResourceList = menuIdResourceIdMap.entries().stream().map(item -> new MenuResourcePO().setRoleId(rolePO.getId()).setTenantMenuId(item.getKey()).setResourceId(item.getValue())).collect(Collectors.toList());

                        //5.4.添加菜单资源中间表
                        iMenuResourceService.saveBatch(addMenuResourceList);
                    }
                }
            }
        }

        //6.返回执行结果
        return result;
    }

    /**
     * 递归获取已选择的菜单ID
     *
     * @param menuIdList
     * @param menuResourceList
     * @return
     */
    private static LinkedList<Long> getSelectedMenuIdList(LinkedList<Long> menuIdList, List<TenantMenuResourceVO> menuResourceList) {
        List<Long> selectIdList = menuResourceList.stream().filter(TenantMenuResourceVO::isSelected).map(TenantMenuResourceVO::getId).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(selectIdList)) {
            menuIdList.addAll(selectIdList);
        }
        menuResourceList.stream()
                .filter(item -> !CollectionUtils.isEmpty(item.getChildren()))
                .forEach(item -> getSelectedMenuIdList(menuIdList, item.getChildren()));
        return menuIdList;
    }

    /**
     * 递归获取已选择的菜单ID、资源ID
     *
     * @param menuIdResourceIdMap
     * @param menuResourceList
     * @return
     */
    private static LinkedHashMultimap<Long, Long> getSelectedMenuIdResourceIdMap(LinkedHashMultimap<Long, Long> menuIdResourceIdMap, List<TenantMenuResourceVO> menuResourceList) {
        menuResourceList.forEach(item -> {
            List<Long> selectResourceList = item.getSelectResourceList();
            if (!CollectionUtils.isEmpty(selectResourceList)) {
                selectResourceList.forEach(resourceId -> menuIdResourceIdMap.put(item.getId(), resourceId));
            }
        });
        menuResourceList.stream()
                .filter(item -> !CollectionUtils.isEmpty(item.getChildren()))
                .forEach(item -> getSelectedMenuIdResourceIdMap(menuIdResourceIdMap, item.getChildren()));
        return menuIdResourceIdMap;
    }

    @Override
    public String getSuperKeyByCurrent() {
        List<Long> roleIdList = JwtUtil.getCurrentUser().getRoleIdList();
        if (!CollectionUtils.isEmpty(roleIdList)) {
            List<RolePO> roleList = this.query().select("role_key").in("id", roleIdList).list();
            if (!CollectionUtils.isEmpty(roleList)) {
                List<String> roleKeyList = roleList.stream().map(RolePO::getRoleKey).collect(Collectors.toList());
                if (roleKeyList.contains(RoleKeyEnum.SUPER.getRoleKey())) {
                    return RoleKeyEnum.SUPER.getRoleKey();
                } else if (roleKeyList.contains(RoleKeyEnum.ADMIN.getRoleKey())) {
                    return RoleKeyEnum.ADMIN.getRoleKey();
                } else if (roleKeyList.contains(RoleKeyEnum.COMMON.getRoleKey())) {
                    return RoleKeyEnum.COMMON.getRoleKey();
                }
            }
        }
        return null;
    }
}